package e_ch13_directedG.graph_final;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Scanner;

// 只支持无权图
public class Graph_final_try implements Cloneable{

    private int V; //可能因为抛异常赋值失败，所以设为final会报错；
    private int E;
    private HashSet<Integer>[] adj;
    private final boolean isDirected; //****

	private int[] indegree, outdegree;// ****  含义是单体词的应该当作一个单词，是组合词才分开用驼峰命名法；
    
    public Graph_final_try(String filename, boolean isDirected){

        File file = new File(filename);
        this.isDirected = isDirected;	//****

        try(Scanner scanner = new Scanner(file)){

            V = scanner.nextInt();
            if(V < 0) throw new IllegalArgumentException("V must be non-negative");
            E = scanner.nextInt();
            if(E < 0) throw new IllegalArgumentException("E must be non-negative");
            
            adj = new HashSet[V];
            for(int i = 0; i < V; i ++)
                adj[i] = new HashSet<Integer>();
            
            this.indegree = new int[V];// ****
            this.outdegree = new int[V];
            

            for(int i = 0; i < E; i ++){
                int a = scanner.nextInt();
                validateVertex(a);
                int b = scanner.nextInt();
                validateVertex(b);

                if(a == b) throw new IllegalArgumentException("Self Loop is Detected!");
                if(adj[a].contains(b)) throw new IllegalArgumentException("Parallel Edges are Detected!");

                adj[a].add(b);// ****
                outdegree[a] ++;
                indegree[b] ++;
                
                if(!isDirected) { //****
                	adj[b].add(a);
                	outdegree[b] ++;
                    indegree[a] ++;
                }
            }
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }

    public Graph_final_try(String graph) {
    	this(graph, false);  //不显示声明，默认无向；
    }

	public void validateVertex(int v){
        if(v < 0 || v >= V)
            throw new IllegalArgumentException("vertex " + v + "is invalid");
    }

    public int getV(){return V;}    
    public int V()	 {return V;}

    public int getE(){return E;}    
    public int E()	 {return E;}

    public boolean hasEdge(int v, int w){
        validateVertex(v);
        validateVertex(w);
        return adj[v].contains(w);
    }

    public Iterable<Integer> adj(int v){
        validateVertex(v);
        return adj[v];
    }

    public int degree(int v){   // ****  undirected  indirect
    	if(isDirected) throw new RuntimeException("function only for undirected graph!");
        validateVertex(v);
        return adj[v].size();
    }
    
    public int indegree(int v) {// ****
    	if(!isDirected) throw new RuntimeException("function only for directed graph!");
    	validateVertex(v);
    	return indegree[v];
	}
    
    public int outdegree(int v) {// ****
    	if(!isDirected) throw new RuntimeException("function only for directed graph!");
    	validateVertex(v);
    	return outdegree[v];
	}
    
	public final boolean isDirected() { // ****
		return isDirected;
	}
	
	    
	public void removeEdge(int v, int w) { // ****
		validateVertex(v);
		validateVertex(w);
		if (adj[v].remove(w)) { // if contained, return true;
			E--;

			if (!isDirected) {
				outdegree[v]--;
				indegree[w]--;
			} else
				adj[w].remove(v);
		}
	}

	public void reverseGraph() {  //计算SCC_related的关键函数；TODO
		
	}
	
	@Override
	public Object clone() { // 如果不实现clonable接口，CloneNotSupportedException就会抛出；
		try {
			Graph_final_try cloned = (Graph_final_try) super.clone(); // super.clone()调用的是native方法
																			// 返回一个当前对象的克隆对象(当前类实现了Clonable接口);
			cloned.adj = new HashSet[V]; // 对浅克隆进行修改，引用类型的指针指向新空间；

			for (int i = 0; i < adj.length; i++) {
				cloned.adj[i] = new HashSet<>(); // 对指针开辟空间
				cloned.adj[i].addAll(adj[i]);
			}

			return cloned;

		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}

		return null;
	}

    @Override
    public String toString(){
        StringBuilder sb = new StringBuilder();

        sb.append(String.format("V = %d, E = %d\n", V, E));
        for(int v = 0; v < V; v ++){
            sb.append(String.format("%d : ", v));
            for(int w : adj[v])
                sb.append(String.format("%d ", w));
            sb.append('\n');
        }
        return sb.toString();
    }

    public static void main(String[] args){

//        Graph_final_try g1 = new Graph_final_try("g2.txt");
//        System.out.print(g1);
////        System.out.print(g1.adj(1));
//        
//        System.out.println();
//        
//        Graph_final_try g1_cloned = (Graph_final_try) g1.clone();
//        System.out.println(g1_cloned);
//        
//        g1.removeEdge(1, 0);
//        System.out.println(g1.adj(1));
//        System.out.println(g1_cloned.adj(1));
        
        
        Graph_final_try g2 = new Graph_final_try("g2.txt", true);
        System.out.print(g2);
        System.out.print(g2.adj(1));
        
        System.out.print("——————————————————");
        System.out.println();

        for (int i = 0; i < g2.getV(); i++) {
        	System.out.println(i+" in: "+g2.indegree(i));
        	System.out.println(i+" out: "+g2.outdegree(i));
		}
        
        
    }
}
